/************************************************************
   Copyright 2008 Abhinav Pobbati

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*************************************************************/

#pragma once

#pragma unmanaged
#include <Common/Base/Memory/Memory/hkMemory.h>
#include <Common/Base/Memory/Memory/Debug/hkDebugMemory.h>
#include <Common/Base/Memory/Memory/Malloc/hkMallocMemory.h>
#include <Common/Base/Memory/Memory/Pool/HkPoolMemory.h>
#include <Common/Base/Memory/hkThreadMemory.h>
#include <Common/Base/Memory/Memory/FreeList/hkLargeBlockAllocator.h>
#include <Common/Base/Memory/Memory/FreeList/hkFreeList.h>
#include <Common/Base/Memory/Memory/FreeList/hkFreeListMemoryServer.h>
#include <Common/Base/Memory/Memory/FreeList/SystemMemoryBlockServer/hkSystemMemoryBlockServer.h>
#include <Common/Base/Memory/Memory/FreeList/FixedMemoryBlockServer/hkFixedMemoryBlockServer.h>
#pragma managed

using System::IntPtr;
using System::Int32;
using System::UInt32;
using System::Runtime::InteropServices::OutAttribute;
using System::Runtime::InteropServices::Marshal;

namespace Havok4Xna {
	public enum class HkMemoryClass : int {
		Root = HK_MEMORY_CLASS_ROOT,
		Base = HK_MEMORY_CLASS_BASE,
		Collide = HK_MEMORY_CLASS_COLLIDE,
		ConstraintSolver = HK_MEMORY_CLASS_CONSTRAINT_SOLVER,
		Dynamics = HK_MEMORY_CLASS_DYNAMICS,
		Export = HK_MEMORY_CLASS_EXPORT,
		Serialize = HK_MEMORY_CLASS_SERIALIZE,
		Geometry = HK_MEMORY_CLASS_GEOMETRY,
		Display = HK_MEMORY_CLASS_DISPLAY,
		Math = HK_MEMORY_CLASS_MATH,
		SceneData = HK_MEMORY_CLASS_SCENE_DATA,
		Vehicle = HK_MEMORY_CLASS_VEHICLE,
		Destruction = HK_MEMORY_CLASS_DESTRUCTION,
		Utilities = HK_MEMORY_CLASS_UTILITIES,
		Animation = HK_MEMORY_CLASS_ANIMATION,
		Behavior = HK_MEMORY_CLASS_BEHAVIOR,
		Cloth = HK_MEMORY_CLASS_CLOTH,
		Tools = HK_MEMORY_CLASS_TOOLS,
		User = HK_MEMORY_CLASS_USER,
		BaseClass = HK_MEMORY_CLASS_BASE_CLASS,
		String = HK_MEMORY_CLASS_STRING,
		Array = HK_MEMORY_CLASS_ARRAY,
		Singleton = HK_MEMORY_CLASS_SINGLETON,
		ScratchPad = HK_MEMORY_CLASS_SCRATCHPAD,
		Stream = HK_MEMORY_CLASS_STREAM,
		Monitor = HK_MEMORY_CLASS_MONITOR,
		Map = HK_MEMORY_CLASS_MAP,
		Tree = HK_MEMORY_CLASS_TREE,
		HkClass = HK_MEMORY_CLASS_HKCLASS,
		Agent = HK_MEMORY_CLASS_AGENT,
		Algorithm = HK_MEMORY_CLASS_ALGORITHM,
		BroadPhase = HK_MEMORY_CLASS_BROAD_PHASE,
		Contact = HK_MEMORY_CLASS_CONTACT,
		Dispatch = HK_MEMORY_CLASS_DISPATCH,
		Manifold = HK_MEMORY_CLASS_MANIFOLD,
		Shape = HK_MEMORY_CLASS_SHAPE,
		Collidable = HK_MEMORY_CLASS_COLLIDABLE,
		CDInfo = HK_MEMORY_CLASS_CDINFO,
		MOPP = HK_MEMORY_CLASS_MOPP,
		Action = HK_MEMORY_CLASS_ACTION,
		DynCollide = HK_MEMORY_CLASS_DYNCOLLIDE,
		Deactivator = HK_MEMORY_CLASS_DEACTIVATOR,
		Constraint = HK_MEMORY_CLASS_CONSTRAINT,
		Entity = HK_MEMORY_CLASS_ENTITY,
		Motion = HK_MEMORY_CLASS_MOTION,
		World = HK_MEMORY_CLASS_WORLD,
		RigidBody = HK_MEMORY_CLASS_RIGID_BODY,
		Phantom = HK_MEMORY_CLASS_PHANTOM,
		Simisland = HK_MEMORY_CLASS_SIMISLAND,
		Camera = HK_MEMORY_CLASS_CAMERA,
		Character = HK_MEMORY_CLASS_CHARACTER,
		VDB = HK_MEMORY_CLASS_VDB,
		Inertia = HK_MEMORY_CLASS_INERTIA,
		AnimUncompressed = HK_MEMORY_CLASS_ANIM_UNCOMPRESSED,
		AnimCompressed = HK_MEMORY_CLASS_ANIM_COMPRESSED,
		AnimData = HK_MEMORY_CLASS_ANIM_DATA,
		AnimMotion = HK_MEMORY_CLASS_ANIM_MOTION,
		AnimRig = HK_MEMORY_CLASS_ANIM_RIG,
		AnimControl = HK_MEMORY_CLASS_ANIM_CONTROL,
		AnimRuntime = HK_MEMORY_CLASS_ANIM_RUNTIME,
		AnimCache = HK_MEMORY_CLASS_ANIM_CACHE,
		BehaviorRuntime = HK_MEMORY_CLASS_BEHAVIOR_RUNTIME,
		ClothRuntime = HK_MEMORY_CLASS_CLOTH_RUNTIME,
		ClothData = HK_MEMORY_CLASS_CLOTH_DATA,
		User1 = HK_MEMORY_CLASS_USER_1,
		User2 = HK_MEMORY_CLASS_USER_2,
		User3 = HK_MEMORY_CLASS_USER_3,
		User4 = HK_MEMORY_CLASS_USER_4,
		PreAllocated = HK_MEMORY_CLASS_PRE_ALLOCATED
	};


	public delegate void HkMemoryWalkCallback(IntPtr start, UInt32 size, bool allocated, int pool, IntPtr param);

	public ref class HkMemory abstract : public INativeReference {
	public:
		void AllocateChunk(int nbytes, HkMemoryClass cl);
		void DeallocateChunk(IntPtr data, int nbytes, HkMemoryClass cl);
		IntPtr AllocateRuntimeBlock(int nbytes, HkMemoryClass cl);
		void DeallocateRuntimeBlock(IntPtr data, int nbytes, HkMemoryClass cl);
		void PreAllocateRuntimeBlock(int nbytes, HkMemoryClass cl);
		void ProvideRuntimeBlock(IntPtr data, int nbytes, HkMemoryClass cl);
		void FreeRuntimeBlocks();
		void ReservePagesFromSystemMemory(int numBytes);
		void GarbageCollect();
		void Optimize();
		void ReleaseUnusedPagesToSystemMemory();
		int GetAllocatedSize(int size);
		bool IsDebugMemory();
		void SetMemoryHardLimit(UInt32 maxMemory);
		UInt32 GetMemoryHardLimit();
		void SetMemorySoftLimit(UInt32 maxMemory);
		UInt32 GetMemorySoftLimit();
		bool HasMemoryAvailable(UInt32 size);
		bool IsOk();
		IntPtr FindBaseAddress(IntPtr p, int nbytes);
		void LockBlock(IntPtr p);
		void UnlockBlock(IntPtr p);
		HkResult WalkMemory(HkMemoryWalkCallback ^callback, IntPtr param);

	internal:
		static HkMemoryWalkCallback ^memoryWalkCallback;
		HK_CLASS_INTERNALS(HkMemory,hkMemory)
	};


	public ref class HkThreadMemory : public INativeReference  {
	public:
		HkThreadMemory(HkMemory ^mainMemoryManager);
		HkThreadMemory(HkMemory ^mainMemoryManager, int maxNumElemsOnFreeList);
		IntPtr Allocate(int nbytes, HkMemoryClass cl);
		void Deallocate(IntPtr data);
		IntPtr AllocateChunkConstSize(int nbytes, HkMemoryClass cl);
		void DeallocateChunkConstSize(IntPtr data, int nbytes, HkMemoryClass cl);
		IntPtr AllocateStack(int nbytes);
		void ShrinkAllocatedStack(IntPtr data, int newSize);
		void DeallocateStack(IntPtr data);
		IntPtr AlignedAllocate(int alignment, int nbytes, HkMemoryClass cl);
		void AlignedDeallcate(IntPtr data);
		void SetStackArea(IntPtr buf, int nbytes);
		void AddReference();
		void RemoveReference();
		void ReleaseCachedMemory();
		int GetRow(int nbytes);
		int RowToSize(int row);

		static HkThreadMemory ^GetInstance();

	internal:
		static HkThreadMemory ^Instance;
		HK_CLASS_INTERNALS(HkThreadMemory,hkThreadMemory)
	};


	public ref class HkDebugMemory : public HkMemory {
	public:
		HkDebugMemory();

	internal:
		HK_DERIVED_CLASS_INTERNALS(HkDebugMemory, hkDebugMemory, HkMemory)
	};


	public ref class HkMallocMemory : public HkMemory {
	public:
		HkMallocMemory();

	internal:
		HK_DERIVED_CLASS_INTERNALS(HkMallocMemory, hkMallocMemory, HkMemory)
	};


	public ref class HkPoolMemory : public HkMemory {
	public:
		HkPoolMemory();

	internal:
		HK_DERIVED_CLASS_INTERNALS(HkPoolMemory, hkPoolMemory, HkMemory)
	};


	public ref class HkMemoryBlockServer abstract : public INativeReference {
	public:
		bool IsSingleBlockServer();
		UInt32 RecommendSize(UInt32 size);
		IntPtr Allocate(UInt32 size, [Out] UInt32 sizeOut);
		void Free(IntPtr data, UInt32 size);
		bool Resize(IntPtr data, UInt32 oldSize, UInt32 newSize, [Out] UInt32 sizeOut);
		UInt32 GetMinAllocSize();
		UInt32 GetTotalAvailableMemory();
		UInt32 GetMemoryLimit();
		bool SetMemoryLimit(UInt32 size);
		IntPtr CriticalAlloc(UInt32 size);
		void CriticalFree(IntPtr ptr, UInt32 size);

	internal:
		HK_CLASS_INTERNALS(HkMemoryBlockServer,hkMemoryBlockServer)
	};
	
	
	public ref class HkFreeListMemoryServer abstract : public INativeReference {
	public:
		IntPtr Alloc(UInt32 size);
		void Free(IntPtr data);
		bool IsValidAlloc(IntPtr data);
		UInt32 GetAllocSize(IntPtr p, UInt32 size);
		UInt32 GetAllocTotalSize(IntPtr p, UInt32 size);
		IntPtr CriticalAlloc(UInt32 size);
		void CriticalFree(IntPtr ptr, UInt32 size);

	internal:
		HkFreeListMemoryServer() {}
		HK_CLASS_INTERNALS(HkFreeListMemoryServer,hkFreeListMemoryServer)
	};


	public ref class HkDefaultFreeListMemoryServer : public HkFreeListMemoryServer {
	public:
		static HkDefaultFreeListMemoryServer ^GetInstance();

	private:
		HkDefaultFreeListMemoryServer() {}
		static HkDefaultFreeListMemoryServer ^Instance;

	internal:
		HK_DERIVED_CLASS_INTERNALS(HkDefaultFreeListMemoryServer, hkDefaultFreeListMemoryServer, HkFreeListMemoryServer)
	};


	public ref class HkFreeList : public INativeReference {
	public:
		HkFreeList(UInt32 elementSize, UInt32 alignment, UInt32 blockSize, HkFreeListMemoryServer ^alloc);
		IntPtr Alloc();
		void Free(IntPtr data);
		void FreeAllMemory();
		void FreeAll();
		UInt32 GetElementSize();
		bool IsOk();
		bool IsFreeElementAvailable();
		int FindGarbage();
		void GarbageCollect();
		int FreeAllFreeBlocks();
		bool HasFreeBlocks();
		void FreeSmallFreeBlocks(UInt32 minSize);
		UInt32 GetNumFreeElements();
		UInt32 GetTotalNumElements();
		UInt32 GetNumUsedElements();
		UInt32 GetBlockSize();
		int GetTotalNumBlocks();
		void WalkMemory(HkMemoryWalkCallback ^callback, int pool, IntPtr param);

	internal:
		static HkMemoryWalkCallback ^memoryWalkCallback;
		HK_CLASS_INTERNALS(HkFreeList,hkFreeList)
	};


	public ref class HkFixedMemoryBlockServer: public HkMemoryBlockServer {
	public:
		HkFixedMemoryBlockServer(IntPtr start, UInt32 size);
	};

	public ref class HkSystemMemoryBlockServer: public HkMemoryBlockServer {
	public:
		HkSystemMemoryBlockServer();
		HkSystemMemoryBlockServer(UInt32 minBlockSize);
	};
}